
package org.apache.osgimaker.analyse.algorithm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.apache.osgimaker.analyse.algorithm.graph.AtomicVertex;
import org.apache.osgimaker.analyse.algorithm.graph.GraphProcessor;
import org.apache.osgimaker.analyse.algorithm.graph.Vertex;


/**
 * Processor which extracts the package dependency graph from the class 
 * dependency graph.
 * 
 */
public class PackageProcessor extends GraphProcessor
{
  private static final class Arc
  {
    final AtomicVertex tail;
    final AtomicVertex head;
    final boolean internalHeadClass;
    Arc(AtomicVertex tail, AtomicVertex head, boolean internalHeadClass)
    {
      this.tail = tail;
      this.head = head;
      this.internalHeadClass = internalHeadClass;
    }
    void create()
    {
      if (internalHeadClass || head.isGraphVertex() == false)
      {
        tail.addOutgoingArcTo(head);
      }
    }
  }
  
  private final HashMap _packageVertices = new HashMap();
  private final List _arcs = new ArrayList();
  private AtomicVertex[] _packageGraph;
  
  /**
   * Returns the package graph after processing.
   * @return can be <tt>null</tt> before processing.
   */
  public AtomicVertex[] getGraph()
  {
    return _packageGraph;
  }
  
  protected void initializeProcessing(Vertex[] graph)
  {
    _packageVertices.clear();
  }

  protected void processBefore(Vertex vertex)
  {
  }

  protected void processArc(Vertex tail, Vertex head)
  {
    PackageVertex tailPackage = getPackageVertex(tail);
    PackageVertex headPackage = getPackageVertex(head);
    boolean internalHeadClass = ((AtomicVertex) head).isGraphVertex();
    _arcs.add(new Arc(tailPackage, headPackage, internalHeadClass));
  }

  private PackageVertex getPackageVertex(Vertex vertex)
  {
    ClassAttributes classAttributes = (ClassAttributes) vertex.getAttributes();
    String className = (classAttributes).getName();
    int index = className.lastIndexOf('.');
    String packageName = index < 0 ? "(default package)" 
                                   : className.substring(0, index);
    PackageVertex result = (PackageVertex) _packageVertices.get(packageName);
    if (result == null)
    {
      result = new PackageVertex(packageName);
      _packageVertices.put(packageName, result);
    }
    if (isVertexFromGraph(vertex))
    {
      // not an external package
      result.reset();
    }
    result.addClass(classAttributes);
    return result;
  }

  private boolean isVertexFromGraph(Vertex vertex)
  {
    return vertex instanceof AtomicVertex
            && ((AtomicVertex) vertex).isGraphVertex();
  }

  protected void processAfter(Vertex vertex)
  {
  }

  protected void finishProcessing(Vertex[] graph)
  {
    for (int i = 0; i < _arcs.size(); i++)
    {
      ((Arc) _arcs.get(i)).create();
    }
    Iterator vertices = _packageVertices.values().iterator();
    ArrayList list = new ArrayList();
    
    while (vertices.hasNext())
    {
      AtomicVertex vertex = (AtomicVertex) vertices.next();
      if (vertex.isGraphVertex())
      {
        list.add(vertex);
      }
    }
    _packageGraph = (AtomicVertex[]) list.toArray(new AtomicVertex[list.size()]);
  }

}
